package cn.krui.helper.db;

import java.util.HashMap;

import org.apache.ibatis.builder.BuilderException;
/**
 * @author zc
 */
public class ParameterExpression extends HashMap<String, String> {
    private static final long serialVersionUID = -2417552199605158680L;
    
    public ParameterExpression(String expression) {
      parse(expression);
    }
    
    private void parse(String expression) {
      int p = skipWS(expression, 0);
      if (expression.charAt(p) == '(') {
        expression(expression, p + 1);
      } else {
        property(expression, p);
      } 
    }
    
    private void expression(String expression, int left) {
      int match = 1;
      int right = left + 1;
      while (match > 0) {
        if (expression.charAt(right) == ')') {
          match--;
        } else if (expression.charAt(right) == '(') {
          match++;
        } 
        right++;
      } 
      put("expression", expression.substring(left, right - 1));
      jdbcTypeOpt(expression, right);
    }
    
    private void property(String expression, int left) {
      if (left < expression.length()) {
        int right = skipUntil(expression, left, ",:");
        put("property", trimmedStr(expression, left, right));
        jdbcTypeOpt(expression, right);
      } 
    }
    
    private int skipWS(String expression, int p) {
      for (int i = p; i < expression.length(); i++) {
        if (expression.charAt(i) > ' '){
          return i; 
        }
      } 
      return expression.length();
    }
    
    private int skipUntil(String expression, int p, String endChars) {
      for (int i = p; i < expression.length(); i++) {
        char c = expression.charAt(i);
        if (endChars.indexOf(c) > -1){
          return i; 
        }
      } 
      return expression.length();
    }
    
    private void jdbcTypeOpt(String expression, int p) {
      p = skipWS(expression, p);
      if (p < expression.length()){
        if (expression.charAt(p) == ':') {
          jdbcType(expression, p + 1);
        } else if (expression.charAt(p) == ',') {
          option(expression, p + 1);
        } else {
          throw new BuilderException("Parsing error in {" + expression + "} in position " + p);
        }  
      }
    }
    
    private void jdbcType(String expression, int p) {
      int left = skipWS(expression, p);
      int right = skipUntil(expression, left, ",");
      if (right > left) {
        put("jdbcType", trimmedStr(expression, left, right));
      } else {
        throw new BuilderException("Parsing error in {" + expression + "} in position " + p);
      } 
      option(expression, right + 1);
    }
    
    private void option(String expression, int p) {
      int left = skipWS(expression, p);
      if (left < expression.length()) {
        int right = skipUntil(expression, left, "=");
        String name = trimmedStr(expression, left, right);
        left = right + 1;
        if (expression.charAt(left) == '"') {
          right = skipUntil(expression, left + 1, "\"");
          while (right + 1 < expression.length() && 
            expression.charAt(right + 1) == '"') {
            right += 2;
            right = skipUntil(expression, right, "\"");
          } 
        } else if (expression.charAt(left) == '\'') {
          right = skipUntil(expression, left + 1, "'");
          while (right + 1 < expression.length() && 
            expression.charAt(right + 1) == '\'') {
            right += 2;
            right = skipUntil(expression, right, "'");
          } 
        } 
        right = skipUntil(expression, right, ",");
        String value = trimmedStr(expression, left, right);
        put(name, value);
        option(expression, right + 1);
      } 
    }
    
    private String trimmedStr(String str, int start, int end) {
      while (str.charAt(start) <= ' '){
        start++; 
      }
      while (str.charAt(end - 1) <= ' '){
        end--; 
      }
      if (str.charAt(start) == '"' && str.charAt(end - 1) == '"') {
        start++;
        end--;
        if (start >= end){
          return ""; 
        }
        return str.substring(start, end).replace("\"\"", "\"");
      } 
      if (str.charAt(start) == '\'' || str.charAt(end - 1) == '\'') {
        start++;
        end--;
        if (start >= end){
          return ""; 
        }
        return str.substring(start, end).replace("''", "'");
      } 
      return (start >= end) ? "" : str.substring(start, end);
    }
  }